package org.videopractice.network.handler;

import com.google.gson.Gson;

import org.videopractice.common.ClassUtils;
import org.videopractice.common.Logger;
import org.videopractice.network.ChannelManager;
import org.videopractice.network.IoSession;
import org.videopractice.network.ServerManager;
import org.videopractice.network.message.BaseDto;
import org.videopractice.network.message.BaseResponse;
import org.videopractice.network.message.GUID;
import org.videopractice.network.message.Message;
import org.videopractice.network.message.MessageCode;

import java.io.IOException;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;

/**
 * 供应商服务消息处理器
 */
public class SupplierMessageHandler extends SimpleChannelInboundHandler<Message> {

    private ServerManager serverManager = new ServerManager();

    /**
     * 覆盖 channelActive 方法在 channel 被启用的时候触发（在建立连接的时候）
     * 此处用handlerAdded方法是否更合适？
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        if (!ChannelManager.addSession(ctx.channel(), new IoSession(ctx.channel()))) {
            ctx.channel().close();
            Logger.e("链接已存在：channelId: " + ctx.channel().id());
        } else {
            ChannelManager.registerSession(GUID.generateByUUID(), ctx.channel());
        }
        Logger.i(String.format("新的链接激活：%s, 总连接数: %s", ctx.toString(), ChannelManager.getConnectCount()));
    }

    /**
     * 链接失效
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        final Channel channel = ctx.channel();
        IoSession session = ChannelManager.getSession(channel);
        if(session.isLogin()) {
            Logger.e("链接失效: " + session);
            logout(session, channel, ctx);	// 正常登出
        }
    }

    /**
     * 链接异常
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if (cause instanceof IOException) {	// IO异常直接登出，关闭链接
            Channel channel = ctx.channel();
            Logger.e(String.format("客户端连接:%s, 异常：%s", channel.id(), cause.toString()));
            IoSession session = ChannelManager.getSession(channel);
            logout(session, channel, ctx);	// 异常登出
        } else {	// 业务异常已经在process方法里处理
            Logger.i("链接业务异常");
            cause.printStackTrace();
        }
    }

    /**
     * 心跳事件：清除多余链接
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent e = (IdleStateEvent) evt;

            Channel channel = ctx.channel();
            IoSession session = ChannelManager.getSession(channel);
            String remarkString = session != null ? session.toString() : "";

            switch (e.state()) {
                case READER_IDLE:	// 客户端读超时
                    // 未超过重连次数,则发送心跳包重连
//                    HeartBeatRequest heartBeatRequest = new HeartBeatRequest();
//                    heartBeatRequest.setTimestamp(System.currentTimeMillis());
//                    Message message = new Message(MessageCode.HeartBeatRequest, heartBeatRequest.toJson());
//                    serverManager.sendMessage(ctx.channel(), message);

                    // 超过重连次数
                    Logger.i(String.format("客户端读取连接超时, 关闭连接：%s", remarkString));
                    logout(session, channel, ctx);	// 超时登出
                    break;
                case WRITER_IDLE:	// 客户端写超时
//                    Logger.i(String.format("客户端写入连接超时, 关闭连接：%s", remarkString));
//                    logout(session, channel, ctx, SessionCloseReason.OVER_TIME);	// 超时登出
                    break;
            }
        }
    }

    /**
     * 登出注销
     * @param session
     * @param channel
     * @param ctx
     */
    private void logout(IoSession session, Channel channel, ChannelHandlerContext ctx) {
        serverManager.logout(channel);	// 注销通道，包括session
        if(channel.isActive()) {
            ctx.close();
        }
    }



    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Message requestMessage) throws Exception {
        final Channel channel = ctx.channel();
        IoSession session = ChannelManager.getSession(channel);
        Logger.i(String.format("接受到消息, 来自：%s, 地址：%s ", session.getAccount(), session.ipAddress, requestMessage));

        // 前置处理
        if(!preProcess(requestMessage, channel)) {
            return;
        }

        // 业务处理（XXX 高并发情况下，应使用线程池异步处理业务逻辑）
        Message responseMessage = this.process(requestMessage, channel);
        // 向客户端发送消息
        if(responseMessage != null) {
            serverManager.sendMessageNoCache(channel, responseMessage);
        }
    }

    /**
     * 前置处理
     * @param requestMessage
     * @param channel
     * @return
     */
    private boolean preProcess(Message requestMessage, Channel channel) {
        // 报文为空
        if(requestMessage == null) {
            return Boolean.FALSE;
        }

        // 权限请求过滤，如需要登陆后操作的业务可以字这里判断登陆状态
        if(requestMessage.getCode() != MessageCode.HeartBeatRequest) {
            // 心跳不做响应
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }

    /**
     * 业务处理，异常信息尽量在服务实现类里处理，特别是中控客户端有微信分身的情况下，此处无法识别中控客户端是以哪个绑定微信发出的报文
     * @param requestMessage
     * @param channel
     * @return
     */
    private Message process(Message requestMessage, Channel channel) {

        BaseDto request = null;
        if(requestMessage.getCode().isJsonCoverToObject()) {	// 将消息体从JSON格式字符串转换为指定对象
            Class requestObjectClass = requestMessage.getCode().getMessageClass();
            request = (BaseDto) new Gson().fromJson(requestMessage.getBody(), requestObjectClass);
        }

        // 根据消息编码,从spring容器中获取对应的业务处理服务实例
        Class<? extends IService> serviceClass = MessageCode.getServiceClassByCode(requestMessage.getCode());
        if(serviceClass == null) {
            return null;
        }
        IService service = ClassUtils.newInstance(serviceClass);

        BaseResponse response = null;	// 业务响应结果参数
        try {
            return service.process(request, requestMessage, channel);
        } catch(IllegalArgumentException e) {
            Logger.e("参数异常" + e.toString());
            response = new BaseResponse("100016", "参数错误", false);
        } catch(Exception e){
            Logger.e("系统异常" + e.toString());
            response = new BaseResponse("000000", "系统异常，请稍后再试！", false);
        }

        if(response != null && MessageCode.exist(requestMessage.getCode().getRefCode())) {
            Logger.e("响应业务结果：" + response);
            MessageCode responseCode = MessageCode.getMessageCodeByCode(requestMessage.getCode().getRefCode());
            return new Message(responseCode, requestMessage.getMsgId(), response.toJson());
        }
        return null;
    }


}
